﻿
Imports System.IO
Imports planet.storageJobConsumerInternal.diskslice

Namespace storageJobConsumerInternal
    Public Class diskslice2_atom
        Inherits atom

        Public Shared ReadOnly sizelen As Int32 = 0
        Public Shared ReadOnly filenamelen As Int32 = 0
        Public Shared ReadOnly metalen As Int32 = 0
        Private Shared ReadOnly enc As Text.Encoding = Nothing
        Private ReadOnly datafullpath As String = Nothing

        Shared Sub New()
            enc = encoding.utf8()
            sizelen = sizeof((New diskslice2_atom()).size())
            filenamelen = enc.GetBytes(createDatafileName()).Length()
            metalen = filenamelen + diskslice2_atom.sizelen
        End Sub

        Private Sub New()
            MyBase.New(0, 1, 2)
        End Sub

        Private Shared Function createDatafileName() As String
            Return strrplc(createGUID(), "-", emptyString)
        End Function

        Private _filename As String = Nothing
        Private Property filename() As String
            Get
                assert(Not isemptystring(_filename))
                Return _filename
            End Get
            Set(ByVal value As String)
                assert(Not isemptystring(value))
                assert(_filename Is Nothing)
                copy(_filename, value)
            End Set
        End Property

        Private _datafileFullpath As String = Nothing
        Private Function datafileFullpath() As String
            If _datafileFullpath Is Nothing Then
                _datafileFullpath = combineDatafileFullPath(filename())
            End If
            Return _datafileFullpath
        End Function

        Private _size As Int64 = 0
        Public Function size() As Int64
            Return _size
        End Function

        Private Function combineDatafileFullPath(ByVal name As String) As String
            Return combinepath(datafullpath, name)
        End Function

        Public Sub New(ByVal start As UInt64,
                       ByVal metaStart As UInt64,
                       ByVal metaLength As UInt64,
                       ByVal datafullpath As String)
            MyBase.New(start, metaStart, metaLength)
            assert(Not isemptystring(datafullpath))
            copy(Me.datafullpath, datafullpath)
        End Sub

        Public Overrides Function read(ByVal meta As bytes) As Boolean
            Dim i As Int64 = 0
            Return meta.size() = metalen AndAlso
                   convertor.toint64(meta, i) AndAlso
                   i >= 0 AndAlso
                   eva(_size, i) AndAlso
                   true_(Sub()
                             filename() = enc.GetString(+meta, sizelen, filenamelen)
                             If exists() Then
                                 Dim fi As FileInfo = Nothing
                                 fi = New FileInfo(datafileFullpath())
                                 If size() <> fi.Length() Then
                                     raiseError(errorHandle.errorType.warning,
                                                "the size ", size(),
                                                " in meta is not consistant with file length ", fi.Length(),
                                                ", assume the file has been changed",
                                                ", delete and restore as not existing.")
                                     delete()
                                 End If
                             Else
                                 delete()
                             End If
                         End Sub)
        End Function

        Public Function delete() As Boolean
            _size = npos
            If exists() Then
                Try
                    File.Delete(datafileFullpath())
                    Return True
                Catch ex As Exception
                    raiseError(errorHandle.errorType.exclamation,
                               "failed to delete datafile ", datafileFullpath(), ", ex ", ex.Message)
                    Return False
                End Try
            Else
                raiseError(errorHandle.errorType.warning,
                           "datafile ", datafileFullpath(), " is not existing, assume deleted already.")
                Return False
            End If
        End Function

        Private Function open(ByVal mode As FileMode,
                              ByVal access As FileAccess,
                              ByVal share As FileShare,
                              ByRef s As Stream,
                              ByVal createNew As Boolean) As Boolean
            If createNew OrElse exists() Then
                Try
                    s = New FileStream(datafileFullpath(), mode, access, share, diskOperDefaultBuffSize)
                Catch ex As Exception
                    raiseError(errorHandle.errorType.exclamation,
                               "cannot open file ", datafileFullpath(),
                               ", ex ", ex.Message)
                    Return False
                End Try

                Return True
            Else
                delete()
                Return False
            End If
        End Function

        Private Function open_do(ByVal mode As FileMode,
                                 ByVal access As FileAccess,
                                 ByVal share As FileShare,
                                 ByRef s As Stream,
                                 ByVal createNew As Boolean,
                                 ByVal d As _do(Of Boolean)) As Boolean
            assert(Not d Is Nothing)
            If open(mode, access, share, s, createNew) AndAlso d() Then
                Return True
            Else
                closestream(s)
                Return False
            End If
        End Function

        Public Overloads Function read(ByVal result As pointer(Of bytes)) As eventComb
            Dim ec As eventComb = Nothing
            Dim s As Stream = Nothing
            Return chain(as_predict(Function() As Boolean
                                        Return open_do(FileMode.Open,
                                                       FileAccess.Read,
                                                       FileShare.Read,
                                                       s,
                                                       False,
                                                       Function() As Boolean
                                                           Return readstream4(s,
                                                                  holdtime:=diskOperDefaultHoldtimeMs,
                                                                  ec:=ec,
                                                                  result:=result)
                                                       End Function)
                                    End Function),
                         as_eventComb(Function() As eventComb
                                          Return ec
                                      End Function,
                                      True),
                         as_predict(Function() As Boolean
                                        Return closestream(s) AndAlso
                                               (result Is Nothing OrElse ((+result).size() = size()))
                                    End Function))
        End Function

        Private Function write(ByVal buff As bytes, ByVal createNew As Boolean) As eventComb
            Dim ec As eventComb = Nothing
            Dim s As Stream = Nothing
            Return chain(as_predict(Function() As Boolean
                                        Return open_do(ifoper(createNew, FileMode.Create, FileMode.Append),
                                                       FileAccess.Write,
                                                       FileShare.None,
                                                       s,
                                                       createNew,
                                                       Function() As Boolean
                                                           Return writestream4(s,
                                                                               +buff,
                                                                               holdtime:=diskOperDefaultHoldtimeMs,
                                                                               ec:=ec)
                                                       End Function)
                                    End Function),
                         as_eventComb(Function() As eventComb
                                          Return ec
                                      End Function,
                                      True),
                         as_predict(Function() As Boolean
                                        Return eva(_size, ifoper(createNew, buff.size(), size() + buff.size())) AndAlso
                                               closestream(s)
                                    End Function))
        End Function

        Public Function write(ByVal buff As bytes) As eventComb
            Return write(buff, True)
        End Function

        Public Function append(ByVal buff As bytes) As eventComb
            Return write(buff, False)
        End Function

        Public Sub randomDatafile()
            Dim f As String = Nothing
            Do
                f = createDatafileName()
            Loop While exists(f)
            filename() = f
        End Sub

        Public Function filename_bytes() As Byte()
            Return enc.GetBytes(filename())
        End Function

        Private Function exists(ByVal filename As String) As Boolean
            Return File.Exists(combineDatafileFullPath(filename))
        End Function

        Private Function exists() As Boolean
            Return File.Exists(datafileFullpath())
        End Function
    End Class

    Public Class diskslice2
        Inherits diskslice(Of diskslice2_atom)
        Private Const datarootSuffix As String = ".data"

        Public Sub New()
            MyBase.New(True)
        End Sub

        Protected Overrides Function metaLength(ByVal buff As bytes) As UInt64
            Return diskslice2_atom.metalen
        End Function

        Protected Overrides Function meta(ByVal atom As diskslice2_atom, ByVal buff As bytes) As bytes
            atom.randomDatafile()
            Dim rtn As bytes = Nothing
            rtn = convertor.tobytes(buff.size())
            Return rtn + atom.filename_bytes()
        End Function

        Protected Overrides Function read(ByVal atom As diskslice2_atom,
                                          ByVal result As pointer(Of bytes)) As eventComb
            Return atom.read(result)
        End Function

        Protected Overrides Function sizeof(ByVal atom As diskslice2_atom) As Int64
            Return atom.size()
        End Function

        Protected Overrides Function version() As String
            Return "Hzj_jie-Gemini-OneFileEach"
        End Function

        Protected Overrides Function write(ByVal atom As diskslice2_atom, ByVal buff As bytes) As eventComb
            Return atom.write(buff)
        End Function

        Private Sub write_size(ByVal atom As diskslice2_atom)
            assert(Not atom Is Nothing)
            stream().Position() = atom.metaStart()
            writer().Write(atom.size())
        End Sub

        Protected Overrides Function modify(ByVal atom As diskslice2_atom, ByVal buff As bytes) As eventComb
            Dim writesize As Boolean = False
            writesize = (buff.size() <> atom.size())
            Return chain_after(atom.write(buff),
                               Function() As Boolean
                                   If writesize Then
                                       write_size(atom)
                                   End If
                                   Return True
                               End Function)
        End Function

        Protected Overrides Function delete(ByVal atom As diskslice2_atom) As eventComb
            atom.delete()
            Return Nothing
        End Function

        Protected Overrides Function retire() As Boolean
            Dim rtn As Boolean = True
            If Not deleteDirectory(datarootFullpath()) Then
                raiseError(errorHandle.errorType.warning, "failed to delete data folder ", datarootFullpath())
                rtn = False
            End If
            If Not createDataRoot() Then
                raiseError(errorHandle.errorType.warning, "failed to create data folder ", datarootFullpath())
                rtn = False
            End If
            Return rtn
        End Function

        Protected Overrides Function append(ByVal atom As diskslice2_atom,
                                            ByVal buff As bytes) As eventComb
            Return chain_after(atom.append(buff),
                               Function() As Boolean
                                   write_size(atom)
                                   Return True
                               End Function)
        End Function

        Protected Overrides Function atom_ctor(ByVal start As UInt64,
                                               ByVal metaStart As UInt64,
                                               ByVal metaLength As UInt64) As diskslice2_atom
            Return New diskslice2_atom(start, metaStart, metaLength, datarootFullpath())
        End Function

        Private _datarootFullpath As String = Nothing
        Private Function datarootFullpath() As String
            If _datarootFullpath Is Nothing Then
                _datarootFullpath = combinepath(datafullpath(), indexfile() + datarootSuffix)
                assert(createDataRoot())
            End If
            Return _datarootFullpath
        End Function

        Private Function createDataRoot() As Boolean
            Return createDirectory(datarootFullpath())
        End Function

        Public Shared Shadows Function initial(ByVal name As String,
                                               ByVal config As configParser,
                                               ByVal c As diskslice2) As Boolean
            Return diskslice(Of diskslice2_atom).initial(name, config, c)
        End Function
    End Class
End Namespace
